#include <stdio.h>

#include "src/cotask.h"

void *ProcessFunction1(CoTask *task, void *arg)
{
    printf("-->[TASK1] ProcessFunction1 Start task = %p para %d\r\n", task, CO_CAST(int, arg));
    for (int i = 1; i <= 3; i++)
    {
        for (int j = 1; j <= 4; j++)
        {
            int k = i * j;
            printf("-->[TASK1] ProcessFunction1 yield %d * %d = %d\r\n", i, j, k);

            void *ret = CoYieldTask(task, CO_ARG(k));

            printf("-->[TASK1] ProcessFunction1 resume task = %p ret is  %d\r\n", task, CO_CAST(int, ret));
        }
    }
    return NULL;
}

void *ProcessFunction2(CoTask *task, void *arg)
{
    printf("-->[TASK2] ProcessFunction2 Start task = %p para %d\r\n", task, CO_CAST(int, arg));
    for (int i = 1; i <= 5; i++)
    {
        for (int j = 1; j <= 1; j++)
        {
            int k = i + j;
            printf("-->[TASK2] ProcessFunction2 yield %d + %d = %d\r\n", i, j, k);

            void *ret = CoYieldTask(task, CO_ARG(k));

            printf("-->[TASK2] ProcessFunction2 resume task = %p ret is  %d\r\n", task, CO_CAST(int, ret));
        }
    }
    return NULL;
}

void *Fibonacci(CoTask *task, void *arg)
{
    int max = CO_CAST(int, arg);
    int left = 0;
    int right = 1;
    int times = 0;
    printf("-->[TASK3] Fibonacci Start task = %p para %d\r\n", task, max);

    while (true)
    {
        times++;
        int out = left + right;
        left = right;
        right = out;
        if (out + right >= max)
        {
            return CO_ARG(out);
        }
        else
        {
            printf("-->[TASK3] Fibonacci yield %d, %d\r\n", left, right);
            CoYieldTask(task, CO_ARG(out));
        }
    }
    return NULL;
}

int main()
{
    int arg = 123456;
    CoTask *task1 = CoCreateTask(NULL, ProcessFunction1, CO_ARG(arg));
    CoTask *task2 = CoCreateTask(NULL, ProcessFunction2, CO_ARG(arg));
    CoTask *task3 = CoCreateTask(NULL, Fibonacci, CO_ARG(arg));
    printf("[MAIN] we got task = %p\r\n", task1);
    while (CoGetTaskStatus(task1) != CO_TASK_STATUS_FINISH || CoGetTaskStatus(task2) != CO_TASK_STATUS_FINISH ||
           CoGetTaskStatus(task3) != CO_TASK_STATUS_FINISH)
    {
        if (CoGetTaskStatus(task1) != CO_TASK_STATUS_FINISH)
        {
            printf("[MAIN] CoResumeTask task1 = %p with para %d\r\n", task1, arg);
            void *ret = CoResumeTask(task1, CO_ARG(arg));
            printf("[MAIN] resume subtask1 = %d \r\n", CO_CAST(int, ret));
            arg++;
        }
        if (CoGetTaskStatus(task2) != CO_TASK_STATUS_FINISH)
        {
            printf("[MAIN] CoResumeTask task2 = %p with para %d\r\n", task2, arg);
            void *ret = CoResumeTask(task2, CO_ARG(arg));
            printf("[MAIN] resume subtask = %d \r\n", CO_CAST(int, ret));
            arg++;
        }
        if (CoGetTaskStatus(task3) != CO_TASK_STATUS_FINISH)
        {
            void *ret = CoResumeTask(task3, NULL);
            printf("[MAIN] resume subtask get Fib = %d \r\n", CO_CAST(int, ret));
        }
    }

    CoDestroyTask(task1);
    CoDestroyTask(task2);
    CoDestroyTask(task3);
    return 0;
}
